package wili.demo.misc;

import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;

@SuppressWarnings("all")
class LambdaMetafactory_demo {
	public static void main(String[] args) {
		String className = Bean100.class.getName();
		String methodName = "operate";
		try {
			//通过全类名，获取类的实例
			Class clazz = Class.forName(className);
			//获取到类的对象，要求该类必须有无参构造
			Bean100 bean100 = (Bean100) clazz.newInstance();
			//获取方法对象
			Method beanMethod = clazz.getDeclaredMethod(methodName, int.class, int.class, int.class);
			MethodHandles.Lookup lookup = MethodHandles.lookup();
			//指定方法不以反射运行
			MethodHandle beanMethodHand;
			int opt = 2;
			if (opt == 1) {
				beanMethodHand = lookup.unreflect(beanMethod);
			} else {
				MethodType mtype = MethodType.methodType(int.class, int.class, int.class, int.class);
				beanMethodHand = lookup.findVirtual(beanMethod.getDeclaringClass(), beanMethod.getName(), mtype);
			}
			//MethodHandle beanMethodHand = lookup.find(beanMethod);
			//获取方法的类型
			MethodType beanMethodType = beanMethodHand.type();
			//将方法的实例对象类型加到方法类型工厂里
			MethodType factoryType = MethodType.methodType(Operator0.class, beanMethodType.parameterType(0));
			//移除方法里的实例对象类型
			beanMethodType = beanMethodType.dropParameterTypes(0, 1);
			//获取代理对象，注意，第二个参数的字符串必须为函数式接口里的方法名
			Operator0 operator = (Operator0) LambdaMetafactory
					.metafactory(lookup, "toOperate", factoryType, beanMethodType, // 
							beanMethodHand, beanMethodType)
					.getTarget().invokeExact(bean100);
			if (operator instanceof Operator0)
				;
			int operate = operator.toOperate(1, 2, 5);
			System.out.println(operate);
		} catch (Throwable throwable) {
			throwable.printStackTrace();
		}
	}
}

/**
 * 被lambda调用的类和方法
 */
class Bean100 {
	public int operate(int a, int b, int c) {
		return a + b - c;
	}
}

/**
 * 自定义的函数式接口，用于lambda调用
 */
@FunctionalInterface
interface Operator0 {
	/**
	 * 入参应和被lambda调用的方法一致，在本例中是Operation中的operate方法
	 * 
	 * @param a
	 * @param b
	 * @param c
	 * @return 返回值应和被lambda调用的方法一致，在本例中是Operation中的operate方法
	 */
	int toOperate(int a, int b, int c);
}